home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C / Code Resources / PopupCDEF 1.3 / Projects / CDEF / Source / PopupCDEF.c next >
Encoding:
C/C++ Source or Header  |  1995-09-16  |  12.1 KB  |  379 lines  |  [TEXT/CWIE]

  1. /* See the file Distribution for distribution terms.
  2.     (c) Copyright 1994 Ari Halberstadt */
  3.  
  4. /*    This file contains a popup CDEF that uses the file PopupLib.c to handle
  5.     the details of popup menus. This file contains glue to link the Control
  6.     Manager and the routines in PopupLib.c.
  7.  
  8.     950826 aih    - popup glue for debugging works with powerpc
  9.     950630 aih    - moved GetMenuHandle before GetResource
  10.     950621 aih    - converted to mwerks
  11.     941226 aih    - checks menuProc field when loading menu
  12.     941126 aih    - automatically adjusts control's min, max
  13.     940706 aih    - adapted for universal headers
  14.     940315 aih    - added check against kPopupVersion before a CDEF is attached during
  15.                     debugging with PopupCDEFAttach.
  16.                     - Moved description to documentation file.
  17.                     - Uses bullet instead of check mark as mark character when using
  18.                     window font.
  19.                     - Added support for the popupFixedWidth variation code.
  20.     940119 aih    - Added call to GetMenuHandle to allow use of menus created by
  21.                     the application that are not in the resource file. Also fixed
  22.                     use of an already disposed handle in the CDEF's dispose routine.
  23.                     Thanks to Eric Bowman (bobo@reed.edu) for both of these.
  24.     931231 aih    - Returns 1 as part code (same as 7.0 CDEF) instead of inCheckBox
  25.     931226 aih    - Major overhaul. Now supports most of the features of the System 7.0 CDEF.
  26.                     - Added functions for installing a glue handle to the popup CDEF,
  27.                     allowing the CDEF to be debugged from within an application. The only
  28.                     message that can't be debugged this way is the initCntl message.
  29.     931224 aih    - Adapted to use PopupHandle type
  30.                     - Removed dependence on other libraries
  31.                     - Simplified by removing complicated variation setting code
  32.                     based on parsing the title string
  33.     910315 aih    - Fixed bug which caused a crash if the menu resource specified in the
  34.                     control's refCon field couldn't be loaded
  35.     910304 aih    - created this file */
  36.  
  37. #include <limits.h>
  38. #include <Controls.h>
  39. #include <LowMem.h>
  40. #include <Memory.h>
  41. #include <Resources.h>
  42. #include <TextEdit.h>
  43. #include <ToolUtils.h>
  44. #include <Windows.h>
  45. #include "PopupLib.h"
  46.  
  47. #if __MWERKS__
  48.     #include <A4Stuff.h>
  49. #elif THINK_C
  50.     #include <SetUpA4.h>
  51. #endif
  52.  
  53. /* mask for the low 31 bits of the calcCRgns message to CDEFs */
  54. #define calcCRgnsMask        (0x7fffffffL)
  55.  
  56. /* hilite value for a disabled control */
  57. #define kControlDisabled    (255)
  58.  
  59. /* System software version needed to use this CDEF. Tested with systems
  60.     6.0.5, 7.0, and 7.1.1, and 7.5, but may work on systems as early
  61.     as 4.0.1. */
  62. #define kSystemVersion        (0x0605)
  63.  
  64. /* The checkMark character is only present in the Chicago system font,
  65.     whereas the bullet character ('•') is present in most fonts. When the
  66.     popupUseWFont variation code is used, we change the mark character
  67.     from the default checkMark to kBulletMark. */
  68. #define kBulletMark            ('•')
  69.  
  70. /*    When debugging, it's convenient to have the CDEF ignore the
  71.     initCntl message, so that we can send it our own message
  72.     to initialize it when we call PopupCDEFAttach. */
  73. #if DEBUG_CDEF
  74.     #define initCntlMsg        (1234)
  75. #else /* DEBUG_CDEF */
  76.     #define initCntlMsg        initCntl
  77. #endif /* DEBUG_CDEF */
  78.  
  79. /* draw the control */
  80. static void Draw(ControlHandle ctl, PopupHandle popup, short param)
  81. {
  82.     #pragma unused(param)
  83.     Str255 title;
  84.     Rect bounds;
  85.     
  86.     GetControlTitle(ctl, title);
  87.     bounds = (**ctl).contrlRect;
  88.     PopupDrawSet(popup, false);
  89.     PopupTitleSet(popup, title);
  90.     PopupBoundsSet(popup, &bounds);
  91.     PopupCurrentSet(popup, (**ctl).contrlValue);
  92.     PopupEnableSet(popup, (**ctl).contrlHilite != kControlDisabled);
  93.     PopupDrawSet(popup, true);
  94.     PopupCalculate(popup);
  95.     if ((**ctl).contrlVis)
  96.         PopupDraw(popup);
  97. }
  98.  
  99. /* return the part of the control that the point is in */
  100. static long Test(ControlHandle ctl, PopupHandle popup, Point where)
  101. {
  102.     #pragma unused(ctl)
  103.     return(PopupWithin(popup, where) ? kPopupPartCode : 0);
  104. }
  105.  
  106. /* calculate the region containing the control */
  107. static void Calculate(ControlHandle ctl, PopupHandle popup, RgnHandle rgn)
  108. {
  109.     #pragma unused(ctl)
  110.     Rect bounds;
  111.     
  112.     PopupBounds(popup, &bounds);
  113.     RectRgn(rgn, &bounds);
  114. }
  115.  
  116. /* load the menu */
  117. static MenuHandle LoadMenu(short id, Boolean *createdMenu)
  118. {
  119.     MenuHandle    menu;        /* the menu */
  120.     ResErrUPP    errproc;    /* for saving and restoring ResErrProc */
  121.     short            load;        /* for saving and restoring ResLoad */
  122.     
  123.     *createdMenu = false;
  124.  
  125.     /* To allow use of menus created by the application that are not in the
  126.         resource file we look for the menu in the menu list. */
  127.     menu = GetMenuHandle(id);
  128.     if (! menu) {
  129.  
  130.         /* We can tell if the menu hasn't been loaded by a call to GetMenu
  131.             since the menu handle will be a nil resource handle. */
  132.         load = LMGetResLoad();
  133.         LMSetResLoad(false);
  134.         menu = (MenuHandle) GetResource('MENU', id);
  135.         LMSetResLoad(load);
  136.         if (menu && (! *menu || ! (**menu).menuProc)) {
  137.  
  138.             /* menu hasn't been loaded yet, so we have to call GetMenu */
  139.             errproc = LMGetResErrProc();
  140.             LMSetResErrProc(NULL);
  141.             menu = GetMenu(id);
  142.             LMSetResErrProc(errproc);
  143.             *createdMenu = true;
  144.         }
  145.     }
  146.     return(menu);
  147. }
  148.  
  149. /* initialize the control */
  150. static void Initialize(ControlHandle ctl, short var)
  151. {
  152.     MenuHandle    menu;                /* the menu */
  153.     PopupHandle popup;            /* the popup menu */
  154.     Rect            bounds;            /* control's bounding rectangle */
  155.     short            nitems;            /* number of items in menu */
  156.     Boolean        createdMenu;    /* true if we created the menu by calling GetMenu */
  157.     
  158.     menu = LoadMenu((**ctl).contrlMin, &createdMenu);
  159.     if (menu) {
  160.         if ((var & popupUseAddResMenu) != 0)
  161.             AppendResMenu(menu, (**ctl).contrlRfCon);
  162.         bounds = (**ctl).contrlRect;
  163.         popup = PopupBegin((GrafPtr) GetWindowPort((**ctl).contrlOwner), menu, &bounds, ctl);
  164.         if (popup) {
  165.             (**popup).state.createdMenu = createdMenu;
  166.             PopupDrawSet(popup, false);
  167.             PopupTitleWidthSet(popup, (**ctl).contrlMax);
  168.             if (((**ctl).contrlValue & popupTitleNoStyle) == 0)
  169.                 PopupTitleStyleSet(popup, (**ctl).contrlValue >> CHAR_BIT);
  170.             if (((**ctl).contrlValue & popupTitleRightJust) == popupTitleRightJust)
  171.                 PopupJustSet(popup, teFlushRight);
  172.             else if (((**ctl).contrlValue & popupTitleCenterJust) == popupTitleCenterJust)
  173.                 PopupJustSet(popup, teCenter);
  174.             if ((var & popupUseWFont) != 0)
  175.                 PopupMarkSet(popup, kBulletMark);
  176.             PopupTypeInSet(popup, (var & popupTypeIn) != 0);
  177.             PopupFixedWidthSet(popup, (var & popupFixedWidth) != 0);
  178.             PopupWindowFontSet(popup, (var & popupUseWFont) != 0);
  179.             PopupDrawSet(popup, true);
  180.             PopupCalculate(popup);
  181.             nitems = CountMItems(menu);
  182.             (**ctl).contrlMax = nitems;
  183.             (**ctl).contrlMin = (nitems > 0 ? 1 : 0);
  184.             (**ctl).contrlValue = (nitems > 0 ? 1 : 0);
  185.             (**ctl).contrlData = (Handle) popup;
  186.             (**ctl).contrlAction = (ControlActionUPP) -1L;
  187.         }
  188.     }
  189. }
  190.  
  191. /* dispose of the control */
  192. static void Dispose(ControlHandle ctl, PopupHandle popup)
  193. {
  194.     MenuHandle menu;
  195.     Boolean createdMenu;
  196.     
  197.     menu = (**popup).menu;
  198.     createdMenu = (**popup).state.createdMenu;
  199.     PopupEnd(popup);
  200.     if (createdMenu)
  201.         ReleaseResource((Handle) menu);
  202.     (**ctl).contrlData = NULL;
  203. }
  204.  
  205. /* track a mouse click in the control */
  206. static long Track(ControlHandle ctl, PopupHandle popup)
  207. {
  208.     short value;
  209.     short part;
  210.  
  211.     part = PopupSelect(popup);
  212.     value = PopupCurrent(popup);
  213.     (**ctl).contrlValue = value;
  214.     return(part);
  215. }
  216.  
  217. /* entry point for CDEF */
  218. pascal long PopupCDEF(short var, ControlHandle ctl, short msg, long param)
  219. {
  220.     PopupHandle    popup = NULL;
  221.     SysEnvRec world;
  222.     long result = 0;
  223.     short nitems;
  224.  
  225.     /* setup global variable base register */
  226.     #if CDEF
  227.         #if __MWERKS__
  228.             long oldA4 = SetCurrentA4();
  229.         #elif THINK_C
  230.             RememberA0();
  231.             SetUpA4();
  232.         #endif
  233.     #endif
  234.     
  235.     /* check system software */
  236.     (void) SysEnvirons(curSysEnvVers, &world);
  237.     if (world.systemVersion >= kSystemVersion) {
  238.         
  239.         /* execute message */
  240.         popup = (PopupHandle) (**ctl).contrlData;
  241.         if (msg == initCntlMsg) {
  242.             Initialize(ctl, var);
  243.             popup = (PopupHandle) (**ctl).contrlData;
  244.         }
  245.         else if (popup) {
  246.             nitems = CountMItems((**popup).menu);
  247.             (**ctl).contrlMax = nitems;
  248.             (**ctl).contrlMin = (nitems > 0 ? 1 : 0);
  249.             if ((**ctl).contrlValue > (**ctl).contrlMax)
  250.                 (**ctl).contrlValue = (**ctl).contrlMax;
  251.             if ((**ctl).contrlValue < (**ctl).contrlMin)
  252.                 (**ctl).contrlValue = (**ctl).contrlMin;
  253.             switch (msg) {
  254.             case drawCntl:
  255.                 param = LoWord(param); /* see TN196 */
  256.                 Draw(ctl, popup, param);
  257.                 break;
  258.             case testCntl:
  259.                 result = Test(ctl, popup, *(Point *) ¶m);
  260.                 break;
  261.             case calcCRgns:
  262.                 param &= calcCRgnsMask;
  263.                 /* no break */
  264.             case calcCntlRgn:
  265.             case calcThumbRgn:
  266.                 Calculate(ctl, popup, (RgnHandle) param);
  267.                 break;
  268.             case dispCntl:
  269.                 Dispose(ctl, popup);
  270.                 popup = NULL;
  271.                 break;
  272.             case autoTrack:
  273.  
  274.                 /*
  275.                     Some posters to Usenet have requested a means to determine if no item was
  276.                     selected from a popup menu. I have tried to address this request by adding
  277.                     the part code kPopupPartNotSelected, which is returned when no selection is
  278.                     made from a menu. However, I cannot seem to get TrackControl to return this
  279.                     result to the application. The user should therefore check the value of the
  280.                     control before and after the call to TrackControl to determine if a different
  281.                     item was selected. Alternatively, the user can check the low word of the
  282.                     low-memory global MenuDisable. If the low-word of MenuDisable is zero, then
  283.                     no item was selected from the popup menu.
  284.  
  285.                     From TechNote TB31:
  286.  
  287.                     _TrackControl
  288.                     When a user clicks on your control, you normally call _TrackControl, which is
  289.                     supposed to return zero if the user does not change the control’s setting or
  290.                     the part code if the user does change the setting.  For 'CDEF' resources that
  291.                     implement custom dragging, _TrackControl returns zero whether or not the user
  292.                     changes the control’s setting.  To work around this problem, you must use
  293.                     another method to find out if the user has changed the control’s setting, such
  294.                     as comparing the control’s value before and after the call to _TrackControl.
  295.  
  296.                     It's not 100% clear to me what this note is saying. Do they mean a custom
  297.                     dragging routine like dragCntl, or actually (and most likely) a custom tracking
  298.                     routine? Also, I observed (in System 7.5 on a PowerPC) that TrackControl actually
  299.                     sends the control a testCntl message before sending it the autoTrack message.
  300.                     Whatever value is returned by the testCntl message is returned as the result of
  301.                     TrackControl, regardless of the value returned by the autoTrack message. */
  302.  
  303.                 param = LoWord(param); /* see TN196 */
  304.                 result = Track(ctl, popup);
  305.                 break;
  306.             }
  307.         }
  308.     }
  309.  
  310.     /* restore global variable base register */
  311.     #if CDEF
  312.         #if __MWERKS__
  313.             SetA4(oldA4);
  314.         #elif THINK_C
  315.             RestoreA4();
  316.         #endif
  317.     #endif
  318.  
  319.     return(result);
  320. }
  321.  
  322. #if CDEF
  323.  
  324. pascal long main(short var, ControlHandle ctl, short msg, long param)
  325. {
  326.     return(PopupCDEF(var, ctl, msg, param));
  327. }
  328.  
  329. #else /* CDEF */
  330.  
  331. /*    Functions for attaching a glue handle so the CDEF can be debugged
  332.     from within an application. */
  333.  
  334. /* Structure installed in the contrlDefProc field. */
  335. typedef struct {
  336.     PopupGlueUnion glue;        /* glue stuff */
  337.     Handle contrlDefProc;    /* saved value of contrlDefProc field */
  338. } TestGlueType, *TestGluePtr, **TestGlueHandle;
  339.  
  340. /* Set the control's contrlDefProc field to a small glue handle that will
  341.     jump to PopupCDEF. This makes debugging a lot easier, since you
  342.     can then step through the code with a debugger. */
  343. void PopupCDEFAttach(ControlHandle ctl)
  344. {
  345.     TestGlueHandle binder;
  346.     
  347.     if (! (**ctl).contrlData ||
  348.          PopupVersion((PopupHandle) (**ctl).contrlData) == kPopupVersion)
  349.     {
  350.         binder = (TestGlueHandle) NewHandleClear(sizeof(TestGlueType));
  351.         if (binder) {
  352.             HLock((Handle) binder);
  353.             PopupGlueInit(&(**binder).glue,
  354.                 (ProcPtr) PopupCDEF,
  355.                 uppControlDefProcInfo);
  356.             HUnlock((Handle) binder);
  357.             (**binder).contrlDefProc = (**ctl).contrlDefProc;
  358.             (**ctl).contrlDefProc = (Handle) binder;
  359.             if (! (**ctl).contrlData)
  360.                 (void) PopupCDEF(GetControlVariant(ctl), ctl, initCntlMsg, 0);
  361.         }
  362.     }
  363. }
  364.  
  365. /* Dispose of the glue handle created with PopupCDEFAttach and set the
  366.     control's contrlDefProc field to point to its original value. */
  367. void PopupCDEFDetach(ControlHandle ctl)
  368. {
  369.     TestGlueHandle binder;
  370.     
  371.     if (PopupVersion((PopupHandle) (**ctl).contrlData) == kPopupVersion) {
  372.         binder = (TestGlueHandle) (**ctl).contrlDefProc;
  373.         (**ctl).contrlDefProc = (**binder).contrlDefProc;
  374.         DisposeHandle((Handle) binder);
  375.     }
  376. }
  377.  
  378. #endif /* CDEF */
  379.